package org.third.integration.kubernates.batch.imp;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.third.common.exception.NotFoundException;
import org.third.integration.kubernates.batch.KubeBatch;
import org.third.integration.kubernates.batch.bean.BatchInfo;
import org.third.integration.kubernates.batch.bean.KubeInstanceMetaDTO;
import org.third.integration.kubernates.batch.utils.K8sYamlUtils;
import org.third.integration.kubernates.cmd.KubectlCreate;
import org.third.integration.kubernates.cmd.KubectlDelete;
import org.third.integration.kubernates.cmd.KubectlGet;
import org.third.integration.kubernates.cmd.imp.bean.KubeInstanceInfo;
import org.third.integration.kubernates.cmd.imp.exp.NotValidK8sYamlException;
import org.third.integration.kubernates.cmd.imp.exp.RequestApiServerException;
import org.third.integration.kubernates.cmd.imp.utils.K8S_TYPE_UTILS;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

@Component
public class KubeBatchImp implements KubeBatch{
	
	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	
	@Autowired 
	private KubectlCreate kubectlCreate;
	
	@Autowired 
	private KubectlDelete kubectlDelete;
	
	@Autowired 
	private KubectlGet kubectlGet;
	
	@Autowired
	private ObjectMapper mapper;

	@Override
	public BatchInfo create(List<InputStream> inputSteamList) throws RequestApiServerException {
		
		BatchInfo batchInfo = new BatchInfo();
		
		List<String> yamlContentList = new ArrayList<String>();
		for(InputStream is : inputSteamList){
			try {
				yamlContentList.addAll(K8sYamlUtils.split(is));
			} catch (Exception e) {
				throw new RuntimeException("Failed to split yaml content.", e);
			}
		}
		for(String yamlContent : yamlContentList){
			try {
				
				KubeInstanceInfo kubeInstanceInfo = kubectlCreate.execute(yamlContent);
//				logger.info("type:"+kubeInstanceInfo.getType()+"     StatusCode:  "+kubeInstanceInfo.getStatusCode());
				batchInfo.getKubeInstanceInfoList().add(kubeInstanceInfo);
			} catch (NotValidK8sYamlException e) {
				logger.error("Found invalidate yaml content: ", yamlContent, e);
				batchInfo.setFoundInvalidate(true);
				batchInfo.getInvalidateContentList().add(yamlContent);
				batchInfo.increaseInvalidateCount();
			}
		}
		
		return batchInfo;
		
	}
	
	@Override
	public BatchInfo create(InputStream inputSteam)
			throws RequestApiServerException {
		
		List<InputStream> inputSteamList = new ArrayList<InputStream>();
		inputSteamList.add(inputSteam);
		
		return this.create(inputSteamList);
	}

	@Override
	public BatchInfo get(List<KubeInstanceMetaDTO> kubeInstanceMetaList) throws RequestApiServerException {
		
		BatchInfo batchInfo = new BatchInfo();
		for(KubeInstanceMetaDTO kubeInstanceMeta : kubeInstanceMetaList){
			try {
				KubeInstanceInfo kubeInstanceInfo = kubectlGet.execute(kubeInstanceMeta.getType(), kubeInstanceMeta.getApiVersion(), kubeInstanceMeta.getNamespace(), kubeInstanceMeta.getInstanceName());
				batchInfo.getKubeInstanceInfoList().add(kubeInstanceInfo);
				logger.info("STATUS:CODE"+kubeInstanceInfo.getStatusCode()+"   "+kubeInstanceInfo.getResponseBody());
			} catch (NotValidK8sYamlException e) {
				logger.error("Found invalidate k8s meta: ", kubeInstanceMeta.toString());
				batchInfo.setFoundInvalidate(true);
				batchInfo.getInvalidateContentList().add(kubeInstanceMeta.toString());
				batchInfo.increaseInvalidateCount();
			}
		}
		
		return batchInfo;
	}
	
	@Override
	public BatchInfo delete(List<KubeInstanceMetaDTO> kubeInstanceMetaList) throws RequestApiServerException {
		
		BatchInfo batchInfo = new BatchInfo();
		for(KubeInstanceMetaDTO kubeInstanceMeta : kubeInstanceMetaList){
			try {
				KubeInstanceInfo kubeInstanceInfo = kubectlDelete.execute(kubeInstanceMeta.getType(), kubeInstanceMeta.getApiVersion(), kubeInstanceMeta.getNamespace(), kubeInstanceMeta.getInstanceName());
				batchInfo.getKubeInstanceInfoList().add(kubeInstanceInfo);
				logger.info(kubeInstanceInfo.getResponseBody());
			} catch (NotValidK8sYamlException e) {
				logger.error("Found invalidate k8s meta: ", kubeInstanceMeta.toString());
				batchInfo.setFoundInvalidate(true);
				batchInfo.getInvalidateContentList().add(kubeInstanceMeta.toString());
				batchInfo.increaseInvalidateCount();
			}
		}
		
		return batchInfo;
	}

	@Override
	public BatchInfo get(KubeInstanceMetaDTO kubeInstanceMetaDTO)
			throws RequestApiServerException {
		List<KubeInstanceMetaDTO> kubeInstanceMetaList = new ArrayList<KubeInstanceMetaDTO>();
		kubeInstanceMetaList.add(kubeInstanceMetaDTO);
		return this.get(kubeInstanceMetaList);
	}

	@Override
	public BatchInfo delete(KubeInstanceMetaDTO kubeInstanceMetaDTO)
			throws RequestApiServerException {
		List<KubeInstanceMetaDTO> kubeInstanceMetaList = new ArrayList<KubeInstanceMetaDTO>();
		kubeInstanceMetaList.add(kubeInstanceMetaDTO);
		return this.delete(kubeInstanceMetaList);
	}
	
	@Override
	public BatchInfo getPodsUnderService(KubeInstanceMetaDTO serviceMetaDTO)
			throws RequestApiServerException {		
		List<String> selectorList = getSelectorListBySvcName(serviceMetaDTO);
		logger.debug(Arrays.toString(selectorList.toArray()));
		
		List<String> podNameList = getPodsNameBySelector(serviceMetaDTO, selectorList);
		logger.debug(Arrays.toString(podNameList.toArray()));
		
		List<KubeInstanceMetaDTO> kubeInstanceMetaList = genPodsMeta(serviceMetaDTO, podNameList);
		
		return this.get(kubeInstanceMetaList);
	}

	private List<KubeInstanceMetaDTO> genPodsMeta(
			KubeInstanceMetaDTO serviceMetaDTO, List<String> podNameList) {
		List<KubeInstanceMetaDTO> kubeInstanceMetaList = new ArrayList<KubeInstanceMetaDTO>();
		for(String podName : podNameList){
			KubeInstanceMetaDTO kubeInstanceMetaDTO = new KubeInstanceMetaDTO();
			kubeInstanceMetaDTO.setNamespace(serviceMetaDTO.getNamespace());
			kubeInstanceMetaDTO.setApiVersion(serviceMetaDTO.getApiVersion());
			kubeInstanceMetaDTO.setInstanceName(podName);
			kubeInstanceMetaDTO.setType(K8S_TYPE_UTILS.getType("pod"));
			kubeInstanceMetaList.add(kubeInstanceMetaDTO);
		}
		return kubeInstanceMetaList;
	}

	private List<String> getPodsNameBySelector(
			KubeInstanceMetaDTO serviceMetaDTO, List<String> selectorList) {
		List<String> podNameList = new ArrayList<String>(); 
		
		try {
			for(String selector : selectorList){
	
				String urlArgs = "labelSelector=" + URLEncoder.encode(selector, "UTF-8");
			
				KubeInstanceInfo kubeInstanceInfo = 
						kubectlGet.getAll(K8S_TYPE_UTILS.getType("pod"), serviceMetaDTO.getApiVersion(), serviceMetaDTO.getNamespace(), urlArgs);
				JsonNode items = mapper.readValue(kubeInstanceInfo.getResponseBody().getBytes("UTF-8"), JsonNode.class).get("items");
				Iterator<JsonNode> podIterator = items.iterator();
				JsonNode podNode;
				while(podIterator.hasNext()){
					podNode = podIterator.next();
					String podName = podNode.get("metadata").get("name").asText();
					podNameList.add(podName); 
				}
			}
		}catch (RequestApiServerException e) {
			logger.error("Internal Server Error",e);
			throw new RuntimeException("RequestApiServerException Exception");
		}catch (JsonParseException e) {
			logger.error("Internal Server Error",e);
			throw new RuntimeException("JsonParseException Exception");
		} catch (JsonMappingException e) {
			logger.error("Internal Server Error",e);
			throw new RuntimeException("JsonMappingException Exception");
		} catch (UnsupportedEncodingException e) {
			logger.error("Internal Server Error",e);
			throw new RuntimeException("UnsupportedEncodingException Exception");
		} catch (IOException e) {
			logger.error("Internal Server Error",e);
			throw new RuntimeException("IOException Exception");
		} catch (NotValidK8sYamlException e) {
			logger.error("Internal Server Error",e);
			throw new RuntimeException("NotValidK8sYamlException Exception");
		}
		return podNameList;
	}

	private List<String> getSelectorListBySvcName(KubeInstanceMetaDTO serviceMetaDTO) throws RequestApiServerException{
		List<String> selectorList = new ArrayList<String>();
		try {
				BatchInfo serviceBatchInfo = this.get(serviceMetaDTO);
				 
				KubeInstanceInfo serviceKubeInstanceInfo = serviceBatchInfo.getKubeInstanceInfoList().get(0);
				if(serviceKubeInstanceInfo.getStatusCode() != 200
					&&serviceKubeInstanceInfo.getStatusCode() != 201){
					if(serviceKubeInstanceInfo.getStatusCode()==404){
						throw new NotFoundException(serviceBatchInfo.toString());
					}else{
						throw new RuntimeException(serviceBatchInfo.toString());
					}
				}
				
				JsonNode selector = mapper.readValue(serviceKubeInstanceInfo.getResponseBody().getBytes("UTF-8"), JsonNode.class).get("spec").get("selector");
				Iterator<String> fieldNameiterator = selector.fieldNames();
				while(fieldNameiterator.hasNext()){
					String selectorName = fieldNameiterator.next();
					String selectorValue = selector.get(selectorName).asText();
					selectorList.add(selectorName + "=" + selectorValue);
				}

			}catch (RequestApiServerException e) {
				logger.error("Internal Server Error",e);
				throw new RequestApiServerException("RequestApiServerException Exception");
			}catch (JsonParseException e) {
				logger.error("Internal Server Error",e);
				throw new RuntimeException("JsonParseException Exception");
			} catch (JsonMappingException e) {
				logger.error("Internal Server Error",e);
				throw new RuntimeException("JsonMappingException Exception");
			} catch (UnsupportedEncodingException e) {
				logger.error("Internal Server Error",e);
				throw new RuntimeException("UnsupportedEncodingException Exception");
			} catch (IOException e) {
				logger.error("Internal Server Error",e);
				throw new RuntimeException("IOException Exception");
			}
		
		return selectorList;
	}

}
